using Microsoft.Extensions.DependencyInjection;
using YMJake.Flowable.ExternalWorker.Client.AspNetCore.Configuration;
using YMJake.Flowable.ExternalWorker.Client.AspNetCore.Hosting;
using YMJake.Flowable.ExternalWorker.Client.AspNetCore.Pipeline;
using YMJake.Flowable.ExternalWorker.Client.Core.Client;
using YMJake.Flowable.ExternalWorker.Client.Core.Rest;

namespace YMJake.Flowable.ExternalWorker.Client.AspNetCore.DependencyInjection;

public static class ServiceCollectionExtensions
{
    /// <summary>
    /// Add Flowable External Worker Client to the service collection
    /// </summary>
    public static IServiceCollection AddFlowableExternalWorker(
        this IServiceCollection services,
        Action<FlowableExternalWorkerOptions> configureOptions)
    {
        var options = new FlowableExternalWorkerOptions();
        configureOptions.Invoke(options);
        options.SetupAuthentication();
        
        services.Configure(configureOptions);
        services.AddSingleton(options);
        
        // Register the basic client and REST client
        services.AddSingleton<IExternalWorkerClient>(
            _ => new ExternalWorkerClient(options));
        
        // Register REST client for direct access
        services.AddSingleton<IFlowableExternalWorkerRestClient>(_ =>
        {
            return new FlowableExternalWorkerRestClient(
                options.FlowableHost,
                options.WorkerId ?? Environment.MachineName,
                options.Authentication,
                options.CustomizeClient);
        });
        
        return services;
    }

    /// <summary>
    /// Add a Flowable worker for a specific topic
    /// </summary>
    public static IFlowableWorkerBuilder AddFlowableWorker(
        this IServiceCollection services,
        string topic,
        int numberOfWorkers = 1)
    {
        if (numberOfWorkers <= 0)
            throw new ArgumentOutOfRangeException(nameof(numberOfWorkers), "Number of workers must be greater than 0");

        var builder = new FlowableWorkerBuilder(services, topic);
        
        // Register the builder so it can be retrieved by the hosted service
        services.AddSingleton(builder);
        
        // Register the hosted service if not already registered
        services.AddHostedService<FlowableWorkerHostedService>();
        
        return builder.WithConcurrency(numberOfWorkers);
    }
}